Use case: HAL CORDIC calculation in DMA mode with peripheral (ADC)

In HAL1, to use the CORDIC peripheral together with another peripheral (such as ADC, Timer, etc.) in DMA mode, you can configure the DMA direction as follows:

CORDIC_DMA_DIR_IN or CORDIC_DMA_DIR_OUT enables one-sided stream management by the CORDIC peripheral. CORDIC_DMA_DIR_NONE allows two peripherals to write to and read from CORDIC data registers.

In HAL2, the new functions HAL_CORDIC_Write_DMA() and HAL_CORDIC_Read_DMA() handle DMA write and read operations separately for improved peripheral management.

In this use case, the ADC performs sampling, conversion, and writes the result directly to the CORDIC WDATA register using DMA. The CORDIC peripheral then reads only the result. In HAL1, one-way stream management is implemented by setting the DMA direction argument to CORDIC_DMA_DIR_OUT. In HAL2, the read stream is managed using HAL_CORDIC_Read_DMA() instead.

Sequence diagram

ADC-to-CORDIC streaming with DMA (CORDIC output only)

  @startuml

  participant "User application\n(thread mode)" as appli
  participant "<b>HAL ADC</b>" as adc
  participant "<b>HAL CORDIC</b>" as cordic
  participant "<b>HAL DMA</b>" as dma
  participant "DMA IRQ handler" as dma_isr

  == Initialization / configuration ==
  appli -> adc : HAL_ADC_Init(...)\nHAL_ADC_ConfigChannel(...)
  appli -> cordic : HAL_CORDIC_Init(&hcordic)\nHAL_CORDIC_Configure(&hcordic, &cordic_config)
  appli -> dma : HAL_DMA_Init(ADC DMA)\nHAL_DMA_Init(CORDIC READ DMA)

  == Start streams ==
  appli -> cordic : HAL_CORDIC_Calculate_DMA(&hcordic, NULL, output_data, n, CORDIC_DMA_DIR_OUT)
  appli -> appli : write_addr = &CORDIC->WDATA
  appli -> adc : HAL_ADC_Start_DMA(&hadc, (uint32_t*)write_addr, n)

  == DMA IRQ / callbacks ==
  dma_isr -> dma : DMAx_IRQHandler()\nHAL_DMA_IRQHandler(...)
  dma -> adc : ADC DMA callbacks
  dma -> cordic : CORDIC DMA callbacks
  cordic -> appli : HAL_CORDIC_CalculateCpltCallback(...)\n/ HAL_CORDIC_ErrorCallback(...)
  adc -> appli : HAL_ADC_ConvCpltCallback(...)\n/ HAL_ADC_ErrorCallback(...)

  @enduml

Code snippet

HAL1

HAL2

#define ADC_CONVERTED_DATA_BUFFER_SIZE   ((uint32_t)  32)


ADC_HandleTypeDef hadc4;
DMA_HandleTypeDef handle_GPDMA1_Channel10;
CORDIC_HandleTypeDef hcordic;
DMA_HandleTypeDef handle_GPDMA1_Channel0;



static int32_t output_data[32];
uint32_t  write_addr;




ADC_ChannelConfTypeDef sConfig = {0};

hadc4.Instance = ADC4;
hadc4.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV4;
hadc4.Init.Resolution = ADC_RESOLUTION_12B;
hadc4.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc4.Init.ScanConvMode = ADC4_SCAN_DISABLE;
hadc4.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
hadc4.Init.LowPowerAutoPowerOff = ADC_LOW_POWER_NONE;
hadc4.Init.LowPowerAutoWait = DISABLE;
hadc4.Init.ContinuousConvMode = ENABLE;
hadc4.Init.NbrOfConversion = 1;
hadc4.Init.DiscontinuousConvMode = DISABLE;
hadc4.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc4.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
hadc4.Init.DMAContinuousRequests = ENABLE;
hadc4.Init.TriggerFrequencyMode = ADC_TRIGGER_FREQ_LOW;
hadc4.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;
hadc4.Init.SamplingTimeCommon1 = ADC4_SAMPLETIME_79CYCLES_5;
hadc4.Init.SamplingTimeCommon2 = ADC4_SAMPLETIME_79CYCLES_5;
hadc4.Init.OversamplingMode = DISABLE;
HAL_ADC_Init(&hadc4);

sConfig.Channel = ADC_CHANNEL_4;
sConfig.Rank = ADC4_REGULAR_RANK_1;
sConfig.SamplingTime = ADC4_SAMPLINGTIME_COMMON_1;
sConfig.OffsetNumber = ADC_OFFSET_NONE;
sConfig.Offset = 0;
HAL_ADC_ConfigChannel(&hadc4, &sConfig);












__HAL_RCC_GPDMA1_CLK_ENABLE();

handle_GPDMA1_Channel10.Instance = GPDMA1_Channel10;
handle_GPDMA1_Channel10.Init.Request = GPDMA1_REQUEST_ADC4;
handle_GPDMA1_Channel10.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;
handle_GPDMA1_Channel10.Init.Direction = DMA_PERIPH_TO_MEMORY;
handle_GPDMA1_Channel10.Init.SrcInc = DMA_SINC_FIXED;
handle_GPDMA1_Channel10.Init.DestInc = DMA_DINC_FIXED;
handle_GPDMA1_Channel10.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_WORD;
handle_GPDMA1_Channel10.Init.DestDataWidth = DMA_DEST_DATAWIDTH_WORD;
handle_GPDMA1_Channel10.Init.Priority = DMA_LOW_PRIORITY_LOW_WEIGHT;
handle_GPDMA1_Channel10.Init.SrcBurstLength = 1;
handle_GPDMA1_Channel10.Init.DestBurstLength = 1;
handle_GPDMA1_Channel10.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0|DMA_DEST_ALLOCATED_PORT1;
handle_GPDMA1_Channel10.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;
handle_GPDMA1_Channel10.Init.Mode = DMA_NORMAL;
HAL_DMA_Init(&handle_GPDMA1_Channel10);

HAL_NVIC_SetPriority(GPDMA1_Channel0_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(GPDMA1_Channel0_IRQn);

__HAL_LINKDMA(hadc, DMA_Handle, handle_GPDMA1_Channel10);

__HAL_RCC_GPIOC_CLK_ENABLE();

GPIO_InitStruct.Pin = GPIO_PIN_3;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

hcordic.Instance = CORDIC;
HAL_CORDIC_Init(&hcordic);

CORDIC_ConfigTypeDef cordic_config;
cordic_config.Function         = CORDIC_FUNCTION_SINE;
cordic_config.Precision        = CORDIC_PRECISION_6CYCLES;
cordic_config.Scale            = CORDIC_SCALE_0;
cordic_config.NbWrite          = CORDIC_NBWRITE_1;
cordic_config.NbRead           = CORDIC_NBREAD_1;
cordic_config.InSize           = CORDIC_INSIZE_32BITS;
cordic_config.OutSize          = CORDIC_OUTSIZE_32BITS;
HAL_CORDIC_Configure(&hcordic, &cordic_config);


__HAL_RCC_GPDMA1_CLK_ENABLE()

handle_GPDMA1_Channel0.Instance = GPDMA1_Channel0;
handle_GPDMA1_Channel0.Init.Request = GPDMA1_REQUEST_CORDIC_READ;
handle_GPDMA1_Channel0.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;
handle_GPDMA1_Channel0.Init.Direction = DMA_PERIPH_TO_MEMORY;
handle_GPDMA1_Channel0.Init.SrcInc = DMA_SINC_FIXED;
handle_GPDMA1_Channel0.Init.DestInc = DMA_DINC_INCREMENTED;
handle_GPDMA1_Channel0.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_WORD;
handle_GPDMA1_Channel0.Init.DestDataWidth = DMA_DEST_DATAWIDTH_WORD;
handle_GPDMA1_Channel0.Init.Priority = DMA_LOW_PRIORITY_LOW_WEIGHT;
handle_GPDMA1_Channel0.Init.SrcBurstLength = 1;
handle_GPDMA1_Channel0.Init.DestBurstLength = 1;
handle_GPDMA1_Channel0.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0|DMA_DEST_ALLOCATED_PORT1;
handle_GPDMA1_Channel0.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;
handle_GPDMA1_Channel0.Init.Mode = DMA_NORMAL;
HAL_DMA_Init(&handle_GPDMA1_Channel0);

HAL_NVIC_SetPriority(GPDMA1_Channel10_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(GPDMA1_Channel10_IRQn);

__HAL_LINKDMA(hcordic, hdmaOut, handle_GPDMA1_Channel0);

/* pInBuff is set to NULL as the input data stream to the CORDIC WDATA register will be handled by the ADC,
   DMADirection is set to CORDIC_DMA_DIR_OUT as HAL_CORDIC will handle only the output data stream */
HAL_CORDIC_Calculate_DMA(&hcordic, NULL, output_data, (ADC_CONVERTED_DATA_BUFFER_SIZE), CORDIC_DMA_DIR_OUT);

write_addr = (uint32_t) &(CORDIC->WDATA);


/* pData = (uint32_t *)write_addr: ADC converted values written directly to the CORDIC WDATA register */
HAL_ADC_Start_DMA(&hadc4, (uint32_t *)write_addr, (ADC_CONVERTED_DATA_BUFFER_SIZE));

/* Wait for the callbacks:
   HAL_ADC_ConvCpltCallback
   HAL_ADC_ErrorCallback
   HAL_CORDIC_CalculateCpltCallback
   HAL_CORDIC_ErrorCallback*/
#define OUTPUT_BUFFER_SIZE   ((uint32_t)  32)

ADC_TypeDef *instance = ADC1;
hal_adc_handle_t hADC1;
hal_dma_handle_t hGPDMA1_CH0;
hal_cordic_handle_t hCORDIC;
hal_dma_handle_t hGPDMA1_CH1;
hal_adc_config_t adc_config;
hal_adc_inj_config_t adc_inj_config;

static int32_t output_data[32];
static volatile uint32_t *write_addr;
hal_cordic_buffer_desc_t output_buffer = {
  .p_data = output_data,
  .size_word = OUTPUT_BUFFER_SIZE};

HAL_ADC_Init(&hADC1, HAL_ADC1);

hal_adc_config_t adc_config;
adc_config.clock               = HAL_ADC_CLOCK_ASYNC_DIV4;
adc_config.resolution          = HAL_ADC_RESOLUTION_14_BIT;
adc_config.trigger_freq_mode   = HAL_ADC_TRIGGER_FREQ_HIGH;
adc_config.sampling_mode       = HAL_ADC_SAMPLING_MODE_NORMAL;
adc_config.sampling_time_com1 = HAL_ADC_SAMPLING_TIME_814CYCLES_5;
adc_config.sampling_time_com2 = HAL_ADC_SAMPLING_TIME_814CYCLES_5;
HAL_ADC_SetConfig(&hADC1, &adc_config);

hal_adc_reg_config_t reg_config;
reg_config.trigger_src        = HAL_ADC_REG_TRIG_SOFTWARE_START;
reg_config.trigger_edge       = HAL_ADC_REG_TRIG_EDGE_NONE;
reg_config.sequencer_length   = 1;
reg_config.sequencer_discont  = HAL_ADC_REG_SEQ_DISCONT_DISABLE;
reg_config.continuous         = HAL_ADC_REG_CONV_CONTINUOUS;
reg_config.overrun            = HAL_ADC_REG_OVR_DATA_OVERWRITTEN;
reg_config.data_transfer      = HAL_ADC_REG_DATA_TRANSFER_NONE;
reg_config.sequencer_mode      = HAL_ADC_REG_SEQ_FIXED;
HAL_ADC_REG_SetConfig(&hADC1, &reg_config);


hal_adc_channel_config_t adc_channel_config;
adc_channel_config.group           = HAL_ADC_GROUP_REGULAR;
adc_channel_config.sequencer_rank  = 1;
adc_channel_config.input_mode      = HAL_ADC_IN_SINGLE_ENDED;
adc_channel_config.sampling_time   = HAL_ADC_SAMPLING_TIME_814CYCLES;
HAL_ADC_SetConfigChannel(&hADC1, HAL_ADC_CHANNEL_4, &adc_channel_config);

hal_adc_inj_config_t adc_inj_config;
adc_inj_config.trigger_src = HAL_ADC_INJ_TRIG_SOFTWARE_START;
adc_inj_config.trigger_edge = HAL_ADC_INJ_TRIG_EDGE_NONE;
adc_inj_config.sequencer_length = 1;
adc_inj_config.sequencer_discont = HAL_ADC_INJ_SEQ_DISCONT_DISABLE;
HAL_ADC_INJ_SetConfig(&hADC1, &adc_inj_config);

adc_channel_config.group           = HAL_ADC_GROUP_INJECTED;
HAL_ADC_SetConfigChannel(&hADC1, HAL_ADC_CHANNEL_4 , &adc_channel_config);

HAL_RCC_GPDMA1_EnableClock();

HAL_DMA_Init(&hGPDMA1_CH0, HAL_GPDMA1_CH0);

hal_dma_direct_xfer_config_t xfer_cfg_adc1_dma;
xfer_cfg_adc1_dma.request         = HAL_GPDMA1_REQUEST_ADC1;
xfer_cfg_adc1_dma.direction       = HAL_DMA_DIRECTION_PERIPH_TO_MEMORY;
xfer_cfg_adc1_dma.src_inc         = HAL_DMA_SRC_ADDR_FIXED;
xfer_cfg_adc1_dma.dest_inc        = HAL_DMA_DEST_ADDR_FIXED;
xfer_cfg_adc1_dma.src_data_width  = HAL_DMA_SRC_DATA_WIDTH_WORD;
xfer_cfg_adc1_dma.dest_data_width = HAL_DMA_DEST_DATA_WIDTH_WORD;
xfer_cfg_adc1_dma.priority        = HAL_DMA_PRIORITY_LOW_WEIGHT_LOW;
HAL_DMA_SetConfigPeriphDirectXfer(&hGPDMA1_CH0, &xfer_cfg_adc1_dma);




HAL_CORTEX_NVIC_SetPriority(GPDMA1_CH0_IRQn, HAL_CORTEX_NVIC_PREEMP_PRIORITY_0, HAL_CORTEX_NVIC_SUB_PRIORITY_0);
HAL_CORTEX_NVIC_EnableIRQ(GPDMA1_CH0_IRQn);

HAL_ADC_REG_SetDMA(&hADC1, &hGPDMA1_CH0);

HAL_RCC_GPIOC_EnableClock();

hal_gpio_config_t  gpio_config;
gpio_config.mode        = HAL_GPIO_MODE_ANALOG;
gpio_config.pull        = HAL_GPIO_PULL_NO;
HAL_GPIO_Init(HAL_GPIOC, HAL_GPIO_PIN_3, &gpio_config);

hal_cordic_config_t cordic_config;
HAL_CORDIC_Init(&hCORDIC, HAL_CORDIC);

HAL_RCC_CORDIC_EnableClock();

cordic_config.function       = LL_CORDIC_FUNCTION_MODULUS;
cordic_config.in_width       = HAL_CORDIC_IN_WIDTH_32_BIT;
cordic_config.nb_arg         = HAL_CORDIC_NB_ARG_1;
cordic_config.out_width      = HAL_CORDIC_OUT_WIDTH_32_BIT;
cordic_config.nb_result      = HAL_CORDIC_NB_RESULT_1;
cordic_config.scaling_factor = HAL_CORDIC_SCALING_FACTOR_0;
cordic_config.precision      = HAL_CORDIC_PRECISION_5_CYCLE;
HAL_CORDIC_SetConfig(&hCORDIC,&cordic_config);

HAL_RCC_GPDMA1_EnableClock();

HAL_DMA_Init(&hGPDMA1_CH1, HAL_GPDMA1_CH1);

hal_dma_direct_xfer_config_t xfer_cfg_cordic_rd_dma;
xfer_cfg_cordic_rd_dma.request         = HAL_GPDMA1_REQUEST_CORDIC_RD;
xfer_cfg_cordic_rd_dma.direction       = HAL_DMA_DIRECTION_PERIPH_TO_MEMORY;
xfer_cfg_cordic_rd_dma.src_inc         = HAL_DMA_SRC_ADDR_FIXED;
xfer_cfg_cordic_rd_dma.dest_inc        = HAL_DMA_DEST_ADDR_INCREMENTED;
xfer_cfg_cordic_rd_dma.src_data_width  = HAL_DMA_SRC_DATA_WIDTH_WORD;
xfer_cfg_cordic_rd_dma.dest_data_width = HAL_DMA_DEST_DATA_WIDTH_WORD;
xfer_cfg_cordic_rd_dma.priority        = HAL_DMA_PRIORITY_LOW_WEIGHT_LOW;
HAL_DMA_SetConfigPeriphDirectXfer(&hGPDMA1_CH1, &xfer_cfg_cordic_rd_dma);




HAL_CORTEX_NVIC_SetPriority(GPDMA1_CH1_IRQn, HAL_CORTEX_NVIC_PREEMP_PRIORITY_0, HAL_CORTEX_NVIC_SUB_PRIORITY_0);
HAL_CORTEX_NVIC_EnableIRQ(GPDMA1_CH1_IRQn);

HAL_CORDIC_SetReadDMA(&hCORDIC, &hGPDMA1_CH1);


/* Start reading the CORDIC output in DMA mode, while the input stream is handled by HAL_ADC */
HAL_CORDIC_Read_DMA(&hCORDIC, &output_buffer);

write_addr = HAL_CORDIC_GetWriteAddress(&hCORDIC);

HAL_ADC_Start(hADC);
/* p_data = (uint8_t const *)write_addr: ADC converted values written directly to the CORDIC WDATA register */
HAL_ADC_REG_StartConv_DMA(hADC, (uint8_t const *)write_addr, OUTPUT_BUFFER_SIZE*4);

/* Wait for the callbacks:
   HAL_ADC_REG_DataTransferCpltCallback
   HAL_ADC_ErrorCallback
   HAL_CORDIC_CalculateCpltCallback()
   HAL_CORDIC_ErrorCallback() */

Similarly to this use case, when using the CORDIC peripheral with a DAC in HAL1, the function HAL_CORDIC_Calculate_DMA is used with the CORDIC_DMA_DIR_IN argument. In HAL2, this is replaced by HAL_CORDIC_Write_DMA().

Key changes

  1. HAL2 splits DMA stream management with dedicated APIs: HAL_CORDIC_Write_DMA() and HAL_CORDIC_Read_DMA().

  2. HAL2 provides HAL_CORDIC_GetWriteAddress() to let another peripheral (such as ADC) write directly to the CORDIC input register.

  3. HAL2 configures the CORDIC DMA streams explicitly using HAL_CORDIC_SetReadDMA() (and HAL_CORDIC_SetWriteDMA() when needed).